#pragma once

#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>

using namespace std;

#define PATHNAME getenv("PWD")   //获取当前路径
#define PROJID 0x668   //当前项目的ID
#define SHMSIZE 4096    //共享内存的大小

//获取 key 值
key_t getKey()
{
    key_t key = ftok(PATHNAME, PROJID);
    if(key == -1)
    {
        cerr << "ftok fail!" << endl;
        cerr << "errno " << errno << " | strerror " << strerror(errno) << endl;
        exit(-1);
    }

    return key;
}

//获取 key 值的十六进制形式
string getHEX(key_t key)
{
    char buffer[64] = { 0 };    //缓冲区
    snprintf(buffer, sizeof buffer, "0x%x", key);
    return string(buffer, buffer + strlen(buffer));
}

//创建共享内存助手
int createShmHelper(key_t key, size_t size, int flag)
{
    int shmid = shmget(key, size, flag);
    if(shmid == -1)
    {
        cerr << "shmget fail" << endl;
        cerr << "errno " << errno << " | strerror " << strerror(errno) << endl;
        exit(-1);
    }

    return shmid;
}

//创建共享内存
int createShm(key_t key, size_t size)
{
    //umask(0);   //权限掩码设为0，与默认权限形成对比
    return createShmHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
}

//获取共享内存
int getShm(key_t key, size_t size)
{
    return createShmHelper(key, size, IPC_CREAT);
}

//挂接至共享内存中
char* attachShm(int shmid)
{
    //NULL 让系统抉择共享内存区位置
    //0 为默认读写权限
    char* start = (char*)shmat(shmid, nullptr, 0);
    return start;
}

//拆卸共享内存中的已挂接的进程
void detachShm(char* shmaddr)
{
    int ret = shmdt(shmaddr);
    if(ret == -1)
    {
        cerr << "detachShm fail" << endl;
        cerr << "errno " << errno << " | strerror " << strerror(errno) << endl;
        exit(-1);  
    }
}

//删除共享内存
void delShm(int shmid)
{
    int ret = shmctl(shmid, IPC_RMID, nullptr);
    if(ret == -1)
    {
        cerr << "delShm fail" << endl;
        cerr << "errno " << errno << " | strerror " << strerror(errno) << endl;
        exit(-1);  
    }
}

//封装实现

#define CLIENT 0
#define SEVER 1

class ShmInit
{
public:
    ShmInit(int type)
        :_type(type)
    {
        //根据不同角色，进行不同的初始化
        if(_type == SEVER) _shmid = createShm(getKey(), SHMSIZE);
        else _shmid = getShm(getKey(), SHMSIZE);

        //挂接
        _start = attachShm(_shmid);
    }

    ~ShmInit()
    {
        //取消挂接
        detachShm(_start);

        //判断身份是否需要删除共享内存
        if(_type == SEVER) delShm(_shmid);
    }

    char* getStart() { return _start; }

private:
    key_t _key;
    int _shmid;
    char* _start;
    int _type;  //角色
};